#include<linux/fs.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/miscdevice.h>
#include<linux/slab.h>
#include<linux/gfp.h>
#include<linux/uaccess.h>

#include <linux/init.h>
#include <linux/mm.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xieziao");
MODULE_DESCRIPTION("mmap_interface");

#define BUF_SIZE 0x20000

static int mmap_interface_mmap(struct file *file, struct vm_area_struct *vma)
{
    int ret = 0;
    char *kbuff = NULL;
    kbuff = kzalloc(BUF_SIZE, GFP_KERNEL);
    if (!kbuff) {
        ret = -ENOMEM;
        goto err;
    }

    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long pfn_start = (virt_to_phys(kbuff) >> PAGE_SHIFT) + vma->vm_pgoff;
    unsigned long virt_start = (unsigned long)kbuff + offset;
    unsigned long size = vma->vm_end - vma->vm_start;

    * (u32 *)kbuff = 0x1234;
    printk(KERN_WARNING "write data mmap 0x%lx\n",*((u32 *)kbuff));

    printk(KERN_WARNING"phy: 0x%lx, offset: 0x%lx, size: 0x%lx\n", pfn_start << PAGE_SHIFT, offset, size);

    /**
     * remap_pfn_range - remap kernel memory to userspace
     * @vma: user vma to map to
     * @addr: target user address to start at
     * @pfn: physical address of kernel memory
     * @size: size of map area
     * @prot: page protection flags for this mapping
     *  Note: this is only safe if the mm semaphore is held when called.
    **/

    ret = remap_pfn_range(vma, vma->vm_start, pfn_start, size, vma->vm_page_prot);
    if (ret)
        printk(KERN_WARNING"%s: remap_pfn_range failed at [0x%lx  0x%lx]\n",
            __func__, vma->vm_start, vma->vm_end);
    else
        printk(KERN_WARNING"%s: map 0x%lx to 0x%lx, size: 0x%lx\n", __func__, virt_start,
            vma->vm_start, size);
err:
    return ret;
}

static struct file_operations mmap_interface_ops = {
	.mmap       = mmap_interface_mmap,
	.owner		= THIS_MODULE,
};

static struct miscdevice mmap_interface_dev = {
	MISC_DYNAMIC_MINOR,
	"mmap_interface",
	&mmap_interface_ops,
};

static int __init mmap_interface_init(void)
{
	int ret;
	ret = misc_register(&mmap_interface_dev);
	return ret;
}

static void __exit mmap_interface_exit(void)
{
	misc_deregister(&mmap_interface_dev);
}

module_init(mmap_interface_init);
module_exit(mmap_interface_exit);
